/*

qutexrlib QT-based XML-RPC library
Copyright (C) 2003  P. Oscar Boykin <boykin@pobox.com>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   
*/
   
#include "xrclient.h"

const QString XRClient::USER_AGENT = "QuteXmlRpc/0.0";

XRClient::XRClient(const QUrl& url,
		   QObject * parent,
		  const char * name) : QObject(parent,name),_url(url)
{

  _is_deflated = false;
  _accept_compressed = true;

  if( url.hasPort() ) {
    _http_client.setHost(url.host(), url.port());
  }
  else {
    _http_client.setHost(url.host());
  }

  /*
   * connect some slots to signals!
   */

  connect( &_http_client, SIGNAL(requestFinished(int,bool)),
           this, SLOT(processHttpResponse(int, bool)) );
  connect( &_http_client,
                  SIGNAL(responseHeaderReceived(const Q3HttpResponseHeader &)),
           this, SLOT(processHeaders(const Q3HttpResponseHeader &)) );
}

XRClient::~XRClient()
{
  /* tell the http client to abort all on-going requests
   * this may result in the slot processHttpResponse()
   * being called for the current request.
   */
  _http_client.abort();

  /* now we must delete all the buffers */
  QMap<int,QBuffer*>::iterator it;
  for(it = _buffer_map.begin(); it != _buffer_map.end(); it++) {
    delete it.data();
  }
}

int XRClient::call(const QString& method,
                   const QList<QVariant>& params)
{
    XRMethodCall xml_method_call(method,params);
    
    /*
     * Serialize the request
     */
    QByteArray payload;
    QTextStream payload_stream(payload, IO_WriteOnly);
    payload_stream.setEncoding( QTextStream::UnicodeUTF8 );
    //don't waste bytes on indenting
    xml_method_call.save( payload_stream, 0 );
    
    /* make the buffer and make the call */
    Q3HttpRequestHeader req_head("POST",_url.path());
    req_head.setValue("Host",_url.host());
    req_head.setValue("User-Agent",USER_AGENT);
    if( _accept_compressed ) {
      /* Accept deflated content */
      req_head.setValue("Accept-Encoding","deflate");
    }
    /* XML-RPC *REQUIRES* the following to be set: */
    req_head.setContentLength( payload.size() );
    req_head.setContentType("text/xml");
    
    /* this buffer will hold all the result.  We don't
     * care about seeing any data until we have the whole result
     */
    QBuffer* this_buffer = new QBuffer();
    int http_req_num = _http_client.request(req_head,
		                            payload,
					    this_buffer);
    
    _buffer_map.insert(http_req_num, this_buffer);
     
    return http_req_num;
}

void XRClient::processHeaders(const Q3HttpResponseHeader & resp)
{
  if( resp.hasKey("Content-Encoding") ) {
    if( resp.value("Content-Encoding") == "deflate" ) {
      _is_deflated = true;
    }
    else {
      _is_deflated = false;
    }
  }
  else {
    _is_deflated = false;
  }
}

void XRClient::processHttpResponse(int http_resp, bool error)
{
  
  if( error ) {
             /*
	      * Use the standard fault codes at
	      * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php.
	      */
     emit fault(http_resp,
		XR_TRANSPORT_ERROR,     
		_http_client.errorString() );
  }
  else {
    if( _buffer_map.contains(http_resp) ) {
      XRMethodResponse xml_response;
      QString parse_error_string;
      bool no_parse_error;
      if( !_is_deflated ) {
          no_parse_error = xml_response.setContent(
		                      _buffer_map[http_resp]->buffer(),
		                      &parse_error_string );
      }
      else {
	  /* this is compressed content */
          no_parse_error = xml_response.setContent(
		             qUncompress( _buffer_map[http_resp]->buffer() ),
		             &parse_error_string );
      }
      if( no_parse_error ) {
	  if( xml_response.parseXmlRpc() ) {
              int faultCode;
	      QString faultString;
              if( xml_response.getFault(faultCode,faultString) ) {
	          emit fault(http_resp, faultCode, faultString);
              }
              else {
              /* It looks good! */
	          emit methodResponse(http_resp, xml_response.getResponse() );
              }
	  }
	  else {
             /*
	      * Use the standard fault codes at
	      * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php.
	      */
	     
             emit fault(http_resp,
			XR_SERVER_ERROR_INVALID_XMLRPC,
	                "server error: recieved bad XML-RPC grammar from remote server");
	  }
      }
      else {
         emit fault(http_resp,
			    XR_PARSE_ERROR_NOT_WELL_FORMED,
			    parse_error_string);
      }
    }
    else {
        //QHttp seems to emit a signal as soon as it is created.
	//So, just ignore it if we didn't make it.
    }
  }
  
  /* clean up the memory for the buffer here */
  if( _buffer_map.contains( http_resp ) ) {
    delete _buffer_map[http_resp];
    _buffer_map.remove(http_resp);
  }
}

void XRClient::setUrl(const QUrl& url)
{
  _url = url;
  if( url.hasPort() ) {
    _http_client.setHost(url.host(), url.port());
  }
  else {
    _http_client.setHost(url.host());
  }
}

void XRClient::setUrl(const QString& u)
{
  _url = u;
  if( _url.hasPort() ) {
    _http_client.setHost(_url.host(), _url.port());
  }
  else {
    _http_client.setHost(_url.host());
  }
}
